-----Ulysses and the Golden Fleece-----
A 4am crack                  2020-02-03
---------------------------------------

Name: Ulysses and the Golden Fleece
Version: 1.1
Genre: adventure
Year: 1982
Credits: Bob Davis, Ken Williams
Publisher: On-Line Systems
Platform: Apple ][+ or later
Media: 5.25-inch disk
Sides: 2
OS: DOS 3.3
Previous cracks: none (of this version)

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  no errors, but copy boots to title
  screen then fills screen with garbage
  and reboots

Locksmith Fast Disk Backup
  ditto

EDD 4 bit copy (no sync, no count)
  works

Copy ][+ nibble editor
  looks like a normal disk, which makes
  sense since COPYA thought it copied
  it

Disk Fixer
  normal bootloader and DOS 3.3
  normal disk catalog on track $11 with
    oddly named files, but real

Why didn't COPYA work?
  presumably some runtime protection
  check that is looking for nibbles
  between the sectors, or properties of
  nibbles that COPYA doesn't maintain

Next steps:

  1. Load startup program
  2. Disable the protection check
  3. Declary victory(*)

(*) go to the gym

                   ~

               Chapter 1
    Negative, I Am A Meat Popsicle


[S6,D1=non-working copy]
[S5,D1=my work disk]

]PR#5
...

]CATALOG,S6,D1

C1983 DSR^C#254
470 FREE

ULYSSES
(C) 1982
ON-LINE SYSTEMS
WRITTEN BY BOB DAVIS
  AND KEN WILLIAMS

DOS 3.3 filename tricks feel very 1982.

Inspecting the disk with Copy ][+, I
can see that those are, in fact, real
files which contain Ctrl-H characters
to make them print over the usual file
metadata in the disk catalog. It also
makes them difficult to load manually.

Let's fix that (ON A COPY OF COURSE).

[Copy ][+]
  [RENAME]
    [FILES]
      [SLOT 6, DRIVE 1]
        rename the first file to HELLO
  [CHANGE BOOT PROGRAM}
    [SLOT 6, DRIVE 1]
      change boot program to HELLO

Rebooting the disk confirms that it
still boots as far as it did last time,
so let's go.

]PR#5
...
]CATALOG,S6,D1

C1983 DSR^C#254
470 FREE

 B 004 HELLO
ON-LINE SYSTEMS
WRITTEN BY BOB DAVIS
  AND KEN WILLIAMS

]BLOAD HELLO

; this is a command available only in
; the 64K version of Diversi-DOS,
; which easily prints the address and
; length of the last file you BLOADed
]PAD
A$2515,L$03F0

]CALL -151

*2515L

; prints the title screen (not shown,
; but I can literally type "26A9G" at
; this point and it displays the text
; and returns safely to the monitor)
2515-   20 A9 26    JSR   $26A9

; copy some code to $300 (not shown)
2518-   20 33 26    JSR   $2633

; set up RWTS parameters
; track 4
251B-   A9 04       LDA   #$04
251D-   8D EC B7    STA   $B7EC

; disk volume 0 (wildcard)
2520-   A9 00       LDA   #$00
2522-   8D EB B7    STA   $B7EB

; read into $3000
2525-   A9 00       LDA   #$00
2527-   8D F0 B7    STA   $B7F0
252A-   A9 30       LDA   #$30
252C-   8D F1 B7    STA   $B7F1

; reads #$65 sectors into $3000+ (not
; shown)
252F-   A9 65       LDA   #$65
2531-   20 67 25    JSR   $2567

That's about as far as my non-working
copy gets before rebooting, so we're
getting close to the part that goes
boom.

2534-   20 44 26    JSR   $2644

*2644L

; wipe language card
2644-   AD 83 C0    LDA   $C083
2647-   AD 83 C0    LDA   $C083
264A-   A0 00       LDY   #$00
264C-   84 10       STY   $10
264E-   A9 D0       LDA   #$D0
2650-   85 11       STA   $11
2652-   A9 12       LDA   #$12
2654-   91 10       STA   ($10),Y
2656-   C8          INY
2657-   D0 FB       BNE   $2654
2659-   E6 11       INC   $11
265B-   D0 F7       BNE   $2654
265D-   AD E9 B7    LDA   $B7E9
2660-   4A          LSR
2661-   4A          LSR
2662-   4A          LSR
2663-   4A          LSR
2664-   09 C0       ORA   #$C0

; set reset vectors in language card
; and page 3
2666-   8D FD FF    STA   $FFFD
2669-   8C FC FF    STY   $FFFC
266C-   8C F2 03    STY   $03F2
266F-   8D F3 03    STA   $03F3
2672-   49 A5       EOR   #$A5
2674-   8D F4 03    STA   $03F4
2677-   AD 81 C0    LDA   $C081

; reset I/O vectors
267A-   A9 F0       LDA   #$F0
267C-   85 36       STA   $36
267E-   A9 FD       LDA   #$FD
2680-   85 37       STA   $37
2682-   85 39       STA   $39
2684-   A9 1B       LDA   #$1B
2686-   85 38       STA   $38
2688-   60          RTS

So close, I can smell it.

                   ~

               Chapter 2
    Anyone else want to negotiate?


Continuing from $2537...

; save current track on the stack
2537-   AD EC B7    LDA   $B7EC
253A-   48          PHA

; do a thing
253B-   20 B3 25    JSR   $25B3

; restore track
253E-   68          PLA
253F-   8D EC B7    STA   $B7EC

Well THAT'S totally suspicious. Let's
see what's at $25B3.

*25B3L

; compute a checksum of $2700-$27FF
25B3-   A9 00       LDA   #$00
25B5-   A8          TAY
25B6-   59 00 27    EOR   $2700,Y
25B9-   C8          INY
25BA-   D0 FA       BNE   $25B6

; save it in zero page
25BC-   85 10       STA   $10

; always branches, because the INY set
; the zero flag which exited the loop
; (this is unrelated to the value of
; the checksum we just stored)
25BE-   F0 01       BEQ   $25C1

; this is bogus code, because we just
; branched into the middle of it
25C0-   A9 A9       LDA   #$A9
25C2-   20 59 00    JSR   $0059

So this is fun: branching forward 1
instruction, so if you don't notice,
you start seeing garbage and don't
know why.

Execution actually continues at $25C1.

*25C1L

; calculate another checksum, but only
; every other byte
25C1-   A9 20       LDA   #$20
25C3-   59 00 27    EOR   $2700,Y
25C6-   C8          INY
25C7-   C8          INY
25C8-   D0 F9       BNE   $25C3

; and store that in zero page
25CA-   85 11       STA   $11

; perturb both values to create an
; address, and push it to the stack
25CC-   49 B7       EOR   #$B7
25CE-   48          PHA
25CF-   A5 10       LDA   $10
25D1-   49 11       EOR   #$11
25D3-   48          PHA
25D4-   D0 01       BNE   $25D7

; bogus, because we branched into the
; middle of it
25D6-   4C 60 08    JMP   $0860

*25D7L

; "return" to the address we just
; created and pushed to the stack
25D7-   60          RTS

OK, I don't know where this returns to,
because it's calculated based on the
checksums and EOR instructions. So I
get to calculate it myself.

Some minor modifications of this code:

; store high byte of calculated address
25CE-   8D 01 03    STA   $0301
25D1-   A5 10       LDA   $10
25D3-   49 11       EOR   #$11

; store low byte
25D5-   8D 00 03    STA   $0300

; actually return
25D8-   60          RTS

*25B3G
<immediately returns to monitor>

*10.11

0010- EE 91

The magic zero page values are $EE and
$91.

*300.301

0300- FF 26

The calculated address is $26FF, so
execution continues at $2700 -- the
page we just checksummed.

                   ~

               Chapter 3
        You wanna play it soft,
          We'll play it soft.
        You wanna play it hard,
          Let's play it hard.


*2700L

2700-   CE 03 27    DEC   $2703
2703-   EF          ???
2704-   03          ???
2705-   27          ???

Oh fantastic. Self-modifying the next
instruction, then falling through to
execute it.

OK, I'm going to build a decryption
program at a separate address.

; reproduce code at $2700
0300-   CE 03 27    DEC   $2703
0303-   60          RTS

; save initial code state
*1700<2700.27FFM

; decrypt
*300G

*2700L

2700-   CE 03 27    DEC   $2703
2703-   EE 03 27    INC   $2703
2706-   AD 24 27    LDA   $2724
2709-   49 8A       EOR   #$8A
270B-   D0 01       BNE   $270E
270D-   20 8D 24    JSR   $248D
2710-   27          ???

Progress! But of course, $2703 is more
self-modifying code, and the listing of
$270D is bogus because $270B branched
into the middle of it (to $270E).

*270EL

270E-   8D 24 27    STA   $2724
2711-   D0 01       BNE   $2714

; bogus
2713-   4C A0 25    JMP   $25A0

I'm tired.

*2714L

2714-   A0 25       LDY   #$25
2716-   98          TYA
2717-   59 00 27    EOR   $2700,Y
271A-   99 00 27    STA   $2700,Y
271D-   C8          INY
271E-   D0 F6       BNE   $2716
2720-   88          DEY
2721-   30 01       BMI   $2724

; bogus
2723-   4C 60 ED    JMP   $ED60

So tired.

*2724L

Wait, no, this address was modified
earlier (at $270E). So I get to update
my decryption program at $300 first.

0300-   CE 03 27    DEC   $2703
0303-   EE 03 27    INC   $2703
0306-   AD 24 27    LDA   $2724
0309-   49 8A       EOR   #$8A
030B-   8D 24 27    STA   $2724
030E-   A0 25       LDY   #$25
0310-   98          TYA
0311-   59 00 27    EOR   $2700,Y
0314-   99 00 27    STA   $2700,Y
0317-   C8          INY
0318-   D0 F6       BNE   $0310
031A-   60          RTS

; restore code
*2700<1700.17FFM

; decrypt
*300G

*2724L

; self-modified to fall through here
2724-   EA          NOP

2725-   C8          INY

; Y becomes 0, so this is an RWTS seek
2726-   8C F4 B7    STY   $B7F4

; track 0
2729-   8C EC B7    STY   $B7EC

; seek there
272C-   A9 B7       LDA   #$B7
272E-   A0 E8       LDY   #$E8
2730-   20 D9 03    JSR   $03D9

; turn on drive motor manually
2733-   AE E9 B7    LDX   $B7E9
2736-   BD 89 C0    LDA   $C089,X

; maybe a counter of some kind? or an
; in-game side effect?
2739-   A9 03       LDA   #$03
273B-   8D FD 95    STA   $95FD
273E-   20 90 27    JSR   $2790

*2790L

; don't know
2790-   A9 1C       LDA   #$1C
2792-   8D FF 95    STA   $95FF

; reset data latch
2795-   BD 8E C0    LDA   $C08E,X

; find a $D5 nibble
2798-   BD 8C C0    LDA   $C08C,X
279B-   10 FB       BPL   $2798
279D-   C9 D5       CMP   #$D5
279F-   EA          NOP
27A0-   EA          NOP
27A1-   F0 0F       BEQ   $27B2

; decrement Death Counter
27A3-   CE FE 95    DEC   $95FE
27A6-   D0 F0       BNE   $2798
27A8-   CE FF 95    DEC   $95FF
27AB-   D0 EB       BNE   $2798

; if we never find a $D5 nibble, pop
; the return address and jump to The
; Badlands
27AD-   68          PLA
27AE-   68          PLA
27AF-   4C 63 27    JMP   $2763

; find $AA $96
27B2-   BD 8C C0    LDA   $C08C,X
27B5-   10 FB       BPL   $27B2
27B7-   C9 AA       CMP   #$AA
27B9-   D0 E2       BNE   $279D
27BB-   48          PHA
27BC-   68          PLA
27BD-   BD 8C C0    LDA   $C08C,X
27C0-   10 FB       BPL   $27BD
27C2-   C9 96       CMP   #$96
27C4-   D0 F1       BNE   $27B7

; skip 5 nibbles
27C6-   A0 05       LDY   #$05
27C8-   20 F5 27    JSR   $27F5
27CB-   C9 AA       CMP   #$AA
27CD-   D0 C9       BNE   $2798

; find $AA nibble
27CF-   BD 8C C0    LDA   $C08C,X
27D2-   10 FB       BPL   $27CF
27D4-   C9 AA       CMP   #$AA
27D6-   D0 C0       BNE   $2798
27D8-   48          PHA
27D9-   68          PLA

; find $D5 nibble
27DA-   BD 8C C0    LDA   $C08C,X
27DD-   10 FB       BPL   $27DA
27DF-   C9 D5       CMP   #$D5
27E1-   D0 F7       BNE   $27DA
27E3-   EA          NOP

; find $AA nibble
27E4-   BD 8C C0    LDA   $C08C,X
27E7-   10 FB       BPL   $27E4
27E9-   C9 AA       CMP   #$AA
27EB-   D0 F2       BNE   $27DF
27ED-   EA          NOP

; skip $15B nibbles
27EE-   A0 00       LDY   #$00
27F0-   20 F5 27    JSR   $27F5
27F3-   A0 5B       LDY   #$5B
27F5-   BD 8C C0    LDA   $C08C,X
27F8-   10 FB       BPL   $27F5
27FA-   48          PHA
27FB-   68          PLA
27FC-   88          DEY
27FD-   D0 F6       BNE   $27F5
27FF-   60          RTS

Continuing from $2741...

; count until we find an $FF nibble
; (note: no BPL loop here, so this
; "count" will be sensitive to hidden
; timing bits between nibbles)
2741-   A0 00       LDY   #$00
2743-   BD 8C C0    LDA   $C08C,X
2746-   C8          INY
2747-   C9 FF       CMP   #$FF
2749-   D0 F8       BNE   $2743
274B-   BD 8C C0    LDA   $C08C,X
274E-   C8          INY
274F-   F0 12       BEQ   $2763

; next nibble must be $C9
2751-   C9 C9       CMP   #$C9
2753-   D0 F6       BNE   $274B

; and the "count" must be in a specific
; range
2755-   C0 23       CPY   #$23
2757-   90 0A       BCC   $2763
2759-   C0 29       CPY   #$29
275B-   B0 06       BCS   $2763

; if all goes well, turn off the drive
; motor and jump back to $2700
275D-   BD 88 C0    LDA   $C088,X
2760-   4C 00 27    JMP   $2700

The second time around, the decryption
loop at $2714 becomes an encryption
loop, re-encrypting the protection code
in memory. Also, the re-encryption of
$2724 takes it from #$EA back to #$60,
which means the routine returns to the
caller after re-encrypting itself.
Which is a neat trick, to be honest.

                   ~

               Chapter 4
               Multipass


This protection check doesn't actually
do anything, so on first glance you
might thing you could just NOP out the
call altogether. Hahahahahahahahahahaha
no you can't do that.

Why not? Because the checksum values in
zero page ($10 and $11) are later used
as decryption keys for the game code.
Looking back at the initial caller, we
see this code:

; save current track
2537-   AD EC B7    LDA   $B7EC
253A-   48          PHA

; calculate checksums and call
; protection check (will only return if
; check was successful)
253B-   20 B3 25    JSR   $25B3

; restore current track
253E-   68          PLA
253F-   8D EC B7    STA   $B7EC

; load some more game code (not shown)
2542-   EE EC B7    INC   $B7EC
2545-   A9 00       LDA   #$00
2547-   8D F0 B7    STA   $B7F0
254A-   A9 09       LDA   #$09
254C-   8D F1 B7    STA   $B7F1
254F-   A9 17       LDA   #$17
2551-   20 67 25    JSR   $2567
2554-   20 CA 26    JSR   $26CA

; and now this
2557-   20 DE 25    JSR   $25DE

*25DEL

25DE-   A0 00       LDY   #$00
25E0-   B9 18 3E    LDA   $3E18,Y
25E3-   45 10       EOR   $10     <-- !
25E5-   99 18 3E    STA   $3E18,Y
25E8-   B9 43 09    LDA   $0943,Y
25EB-   45 11       EOR   $11     <-- !
25ED-   99 43 09    STA   $0943,Y
25F0-   C8          INY
25F1-   D0 ED       BNE   $25E0

So that's great.

On the bright side, those appear to be
the only side effects that matter. The
various counters at $95FD-$95FF are
never checked or used for anything. I
can bypass this protection as long as I
set zero page $10 and $11 to the proper
values, so the game code is properly
decrypted.

T17,S0E,$26 ->
  A9 EE 85 10 A9 91 85 11 2C

which turns this:

2537-   AD EC B7    LDA   $B7EC
253A-   48          PHA
253B-   20 B3 25    JSR   $25B3
253E-   68          PLA
253F-   8D EC B7    STA   $B7EC

into this:

2537-   A9 EE       LDA   #$EE
2539-   85 10       STA   $10
253B-   A9 91       LDA   #$91
253D-   85 11       STA   $11
253F-   2C EC B7    BIT   $B7EC

I played through the entire game, and
this crack is complete.

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 2153
------------------EOF------------------
